﻿#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <vector>
#include<list>
#include <functional>
#include <algorithm>

using namespace std;

//int main()
//{
//	auto ret1 = [](int x, int y)->int {return x + y; };
//
//	cout << ret1(1, 2) << endl;
//
//	auto func = []
//	{
//		cout << "hello world" << endl;
//		return 0;
//	};
//
//	func();
//
//	auto swap = [](int& a, int& b) 
//	{
//		int tmp = a;
//		a = b;
//		b = tmp;
//	};
//
//	int x = 10;
//	int y = 20;
//
//	swap(x, y);
//
//	cout << x << " " << y << endl;
//
//	return 0;
//}

//int x = 0;
//
//auto func1 = []
//{
//	++x;
//};
//
//int main()
//{
//	int a = 0, b = 1, c = 2, d = 3;
//
//	auto func2 = [a,&b]
//	{
//		//++a;
//		++b;
//	};
//
//	auto func3 = [=] 
//	{
//		/*++a;
//		++b;
//		++c;
//		++d;*/
//
//		int ret = a + b + c;
//		return ret;
//	};
//
//	auto func4 = [&]
//	{
//		++a;
//		++b;
//		++c;
//		++d;
//	};
//
//	auto func5 = [&, a, b]
//	{
//		/*++a;
//		++b;*/
//		++c;
//		++d;
//	};
//
//	auto func6 = [=, &a, &b] 
//	{
//		++a;
//		++b;
//		/*++c;
//		++d;*/
//	};
//
//	// 局部的静态和全局变量不能捕捉，也不需要捕捉
//	static int m = 5;
//	auto func7 = []
//	{
//		int ret = x + m;
//		return ret;
//	};
//
//
//	// 传值捕捉本质是⼀种拷⻉,并且被const修饰了
//// mutable相当于去掉const属性，可以修改了
//// 但是修改了不会影响外⾯被捕捉的值，因为是⼀种拷⻉
//	auto func7 = [a, b]()mutable
//		{
//			++a;
//			++b;
//		};
//
//	return 0;
//}


//struct Goods
//{
//	string _name; // 名字
//	double _price; // 价格
//	int _evaluate; // 评价
//	// ...
//	Goods(const char* str, double price, int evaluate)
//		:_name(str)
//		, _price(price)
//		, _evaluate(evaluate)
//	{
//	}
//};
//
//int main()
//{
//	vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3}, { "菠萝", 1.5, 4 } };
//
//	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price; });
//
//	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate; });
//
//	return 0;
//}

int f(int a,int b)
{
	return a + b;
}

struct Func
{
	int operator()(int a,int b)
	{
		return a + b;
	}
};

class Plus
{
public:
	Plus(int n=10)
		:_n(n)
	{

	}

	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return (a + b) * _n;
	}

private:
	int _n;
};

//int main()
//{
//	function<int(int, int)>f1 = f;
//	function<int(int, int)>f2 = Func();
//
//	function<int(int, int)>f3 = &Plus::plusi;
//
//	function<double(Plus, double, double)>f4 = &Plus::plusd;
//	Plus pd;
//	cout << f4(pd, 1.1, 2.2) << endl;
//
//	function<double(Plus*, double, double)>f5 = &Plus::plusd;
//	cout << f5(&pd, 2.2, 3.3) << endl;
//
//	function<double(Plus&&, double, double)>f6 = &Plus::plusd;
//	cout << f6(move(pd), 3.3, 4.4) << endl;
//	cout << f6(Plus(), 5.5, 6.6) << endl;
//
//
//	return 0;
//}

//using placeholders::_1;
//using placeholders::_2;
//using placeholders::_3;
//
//int Sub(int a, int b)
//{
//	return (a - b) * 10;
//}
//int SubX(int a, int b, int c)
//{
//	return (a - b - c) * 10;
//}
//class Plus
//{
//public:
//	static int plusi(int a, int b)
//	{
//		return a + b;
//	}
//	double plusd(double a, double b)
//	{
//		return a + b;
//	}
//};
//
//int main()
//{
//	// bind 本质返回的⼀个仿函数对象
//// 调整参数顺序（不常⽤）
//// _1代表第⼀个实参
//// _2代表第⼆个实参
//// ...
//	auto sub1 = bind(Sub, _1, _2);//_1=10=a,_2=5=b
//	sub1(10, 5);
//
//	auto sub2 = bind(Sub, _2, _1);//_2=5=a,_1=10=b
//	sub2(10, 5);
//
//	auto sub3 = bind(Sub, 100, _1);
//	sub3(5);
//
//	auto sub4 = bind(Sub, _1, 100);
//	sub4(5);
//
//	auto sub5 = bind(SubX, 100, _1, _2);
//	sub5(5, 6);
//
//	auto sub5 = bind(SubX, _1, 100, _2);
//	sub5(5, 6);
//
//	auto sub5 = bind(SubX, _1, _2,100);
//	sub5(5, 6);
//
//	function<double(Plus&&, double, double)>f6 = &Plus::plusd;
//	Plus pd;
//	cout << f6(move(pd), 3.3, 4.4) << endl;
//	cout << f6(Plus(), 5.5, 6.6) << endl;
//
//	//将一个参数绑死后，function中就不用写对应的参数类型了
//	function<double(double, double)>f7 = bind(&Plus::plusd, Plus(), _1, _2);
//	cout << f7(1.1, 2.2) << endl;
//
//	return 0;
//}

//被选中的处理代码是调⽤链中与该对象类型匹配且离抛出异常位置最近的那⼀个。根据抛出对象的
//类型和内容，程序的抛出异常部分告知异常处理部分到底发⽣了什么错误。
//当throw执⾏时，throw后⾯的语句将不再被执⾏。程序的执⾏从throw位置跳到与之匹配的catch
//模块，catch可能是同⼀函数中的⼀个局部的catch，也可能是调⽤链中另⼀个函数中的catch，控
//制权从throw位置转移到了catch位置。这⾥还有两个重要的含义：1、沿着调⽤链的函数可能提早
//退出。2、⼀旦程序开始执⾏异常处理程序，沿着调⽤链创建的对象都将销毁。
//• 抛出异常对象后，会⽣成⼀个异常对象的拷⻉，因为抛出的异常对象可能是⼀个局部对象，所以会
//⽣成⼀个拷⻉对象，这个拷⻉的对象会在catch⼦句后销毁。（这⾥的处理类似于函数的传值返
//回）
double Divide(int a, int b)
{
	try
	{
		// 当b == 0时抛出异常
		if (b == 0)
		{
			string s("Divide by zero condition!");
			throw s;
		}
		else
		{
			return ((double)a / (double)b);
		}
	}
	catch (int errid)//就近匹配
	{
		cout << errid << endl;
	}
	return 0;
}
//• 抛出异常后，程序暂停当前函数的执⾏，开始寻找与之匹配的catch⼦句，⾸先检查throw本⾝是否
//在try块内部，如果在则查找匹配的catch语句，如果有匹配的，则跳到catch的地⽅进⾏处理。
//• 如果当前函数中没有try / catch⼦句，或者有try / catch⼦句但是类型不匹配，则退出当前函数，继续
//在外层调⽤函数链中查找，上述查找的catch过程被称为栈展开。
//• 如果到达main函数，依旧没有找到匹配的catch⼦句，程序会调⽤标准库的 terminate 函数终⽌
//程序。
//• 如果找到匹配的catch⼦句处理后，catch⼦句代码会继续执⾏。
//void Func()
//{
//	int len, time;
//	cin >> len >> time;
//	try
//	{
//		cout << Divide(len, time) << endl;
//	}
//	catch (const char* errmsg)
//	{
//		cout << errmsg << endl;
//	}
//	cout << __FUNCTION__ << ":" << __LINE__ << "⾏执⾏" << endl;
//}
//
//int main()
//{
//	while (1)
//	{
//		try
//		{
//			Func();
//		}
//		catch (const string& errmsg)
//		{
//			cout << errmsg << endl;
//		}
//	}
//	return 0;
//}

//⼀般情况下抛出对象和catch是类型完全匹配的，如果有多个类型匹配的，就选择离他位置更近的
//那个。
//• 但是也有⼀些例外，允许从⾮常量向常量的类型转换，也就是权限缩⼩；允许数组转换成指向数组
//元素类型的指针，函数被转换成指向函数的指针；允许从派⽣类向基类类型的转换，这个点⾮常实
//⽤，实际中继承体系基本都是⽤这个⽅式设计的。
//• 如果到main函数，异常仍旧没有被匹配就会终⽌程序，不是发⽣严重错误的情况下，我们是不期望
//程序终⽌的，所以⼀般main函数中最后都会使⽤catch(...)，它可以捕获任意类型的异常，但是是
//不知道异常错误是什么。

// ⼀般⼤型项⽬程序才会使⽤异常，下⾯我们模拟设计⼀个服务的⼏个模块
// 每个模块的继承都是Exception的派⽣类，每个模块可以添加⾃⼰的数据
// 最后捕获时，我们捕获基类就可以

#include <thread>
class Exception
{
public:
	Exception(const string& errmsg, int id)
		:_errmsg(errmsg)
		, _id(id)
	{
	}
	virtual string what() const
	{
		return _errmsg;
	}
	int getid() const
	{
		return _id;
	}
protected:
	string _errmsg;
	int _id;
};

class SqlException : public Exception
{
public:
	SqlException(const string& errmsg, int id, const string& sql)
		:Exception(errmsg, id)
		, _sql(sql)
	{
	}
	virtual string what() const
	{
		string str = "SqlException:";
		str += _errmsg;
		str += "->";
		str += _sql;
		return str;
	}
private:
	const string _sql;
};

class CacheException : public Exception
{
public:
	CacheException(const string& errmsg, int id)
		:Exception(errmsg, id)
	{
	}
	virtual string what() const
	{
		string str = "CacheException:";
		str += _errmsg;
		return str;
	}
};

class HttpException : public Exception
{
public:
	HttpException(const string& errmsg, int id, const string& type)
		:Exception(errmsg, id)
		, _type(type)
	{
	}
	virtual string what() const
	{
		string str = "HttpException:";
		str += _type;
		str += ":";
		str += _errmsg;
		return str;
	}
private:
	const string _type;
};

void SQLMgr()
{
	if (rand() % 7 == 0)
	{
		throw SqlException("权限不⾜", 100, "select * from name = '张三'");
	}
	else
	{
		cout << "SQLMgr 调⽤成功" << endl;
	}
}

void CacheMgr()
{
	if (rand() % 5 == 0)
	{
		throw CacheException("权限不⾜", 100);
	}
	else if (rand() % 6 == 0)
	{
		throw CacheException("数据不存在", 101);
	}
	else
	{
		cout << "CacheMgr 调⽤成功" << endl;
	}
	SQLMgr();
}

void HttpServer()
{
	if (rand() % 3 == 0)
	{
		throw HttpException("请求资源不存在", 100, "get");
	}
	else if (rand() % 4 == 0)
	{
		throw HttpException("权限不⾜", 101, "post");
	}
	else
	{
		cout << "HttpServer调⽤成功" << endl;
	}
	CacheMgr();
}

//int main()
//{
//	srand(time(0));
//	while (1)
//	{
//		this_thread::sleep_for(chrono::seconds(1));
//		try
//		{
//			HttpServer();
//		}
//		catch (const Exception& e) // 这⾥捕获基类，基类对象和派⽣类对象都可以被
//		{
//			cout << e.what() << endl;
//		}
//		catch (...)
//		{
//			cout << "Unkown Exception" << endl;
//		}
//		
//	}
//
//     return 0;
//
//}

//有时catch到⼀个异常对象后，需要对错误进⾏分类，其中的某种异常错误需要进⾏特殊的处理，其他
//错误则重新抛出异常给外层调⽤链处理。捕获异常后需要重新抛出，直接 throw; 就可以把捕获的对
//象直接抛出。

// 下⾯程序模拟展⽰了聊天时发送消息，发送失败补货异常，但是可能在
// 电梯地下室等场景⼿机信号不好，则需要多次尝试，如果多次尝试都发
// 送不出去，则就需要捕获异常再重新抛出，其次如果不是⽹络差导致的
// 错误，捕获后也要重新抛出
// 
//void _SeedMsg(const string& s)
//{
//	if (rand() % 2 == 0)
//	{
//		throw HttpException("⽹络不稳定，发送失败", 102, "put");
//	}
//	else if (rand() % 7 == 0)
//	{
//		throw HttpException("你已经不是对象的好友，发送失败", 103, "put");
//	}
//	else
//	{
//		cout << "发送成功" << endl;
//	}
//
//}
//
//void SeedMsg(const string& s)
//{
// // 发送消息失败，则再重试3次
//	for (int i = 0; i < 4; i++)
//	{
//		try
//		{
//			_SeedMsg(s);
//			break;
//		}	
//
//		catch(const Exception& e)
//		{
// //捕获异常，if中是102号错误，⽹络不稳定，则重新发送
// //捕获异常，else中不是102号错误，则将异常重新抛出
//			if (e.getid() == 102)
//			{
// // 重试三次以后都失败了，则说明⽹络太差了，重新抛出异常
//				if (i == 3)
//				{
//					throw;
//				}
//				cout << "重试第" << i + 1 << "次" << endl;
//			}
//			else
//			{
//				throw;
//			}
//		}
//	}
//}
//
//int main()
//{
//	srand(time(0));
//
//	string str;
//
//	while (cin >> str)
//	{
//		try
//		{
//			SeedMsg(str);
//		}
//		catch (const Exception& e)
//		{
//			cout << e.what() << endl << endl;
//		}
//		catch (...)
//		{
//			cout << "unknown error" << endl << endl;
//		}
//	}
//
//	return 0;
//}


//异常抛出后，后⾯的代码就不再执⾏，前⾯申请了资源(内存、锁等)，后⾯进⾏释放，但是中间可
//能会抛异常就会导致资源没有释放，这⾥由于异常就引发了资源泄漏，产⽣安全性的问题。中间我
//们需要捕获异常，释放资源后⾯再重新抛出
//double Divide(int a, int b)
//{
//	// 当b == 0时抛出异常
//	if (b == 0)
//	{
//		throw "Division by zero condition!";
//	}
//	return (double)a / (double)b;
//}
//
//// 这⾥可以看到如果发⽣除0错误抛出异常，另外下⾯的arr没有得到释放。
//// 所以这⾥捕获异常后并不处理异常，异常还是交给外层处理，这⾥捕获了再
//// 重新抛出去。
//void Func()
//{
//	int* arr = new int[10];
//
//	try
//	{
//		int len, time;
//		cin >> len >> time;
//		cout << Divide(len, time) << endl;
//	}
//	catch (...)
//	{
//		// 捕获异常释放内存
//		cout << "delete []" << arr << endl;
//		delete[] arr;
//		throw;// 异常重新抛出，捕获到什么抛出什么
//	}
//
//	cout << "delete []" << arr << endl;
//	delete[] arr;
//}
//
//int main()
//{
//	try
//	{
//		Func();
//	}
//	catch (const char* errmsg)
//	{
//		cout << errmsg << endl;
//	}
//	catch (const Exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "unknown error" << endl;
//	}
//
//	return 0;
//}

//函数参数列表后⾯加noexcept表⽰不会抛出异常，啥都不加表⽰可能会抛出异常。
//• 编译器并不会在编译时检查noexcept，也就是说如果⼀个函数⽤noexcept修饰了，但是同时⼜包
//含了throw语句或者调⽤的函数可能会抛出异常，编译器还是会顺利编译通过的(有些编译器可能会报个警告)。
// 但是⼀个声明了noexcept的函数抛出了异常，程序会调⽤ terminate 终⽌程序。
//• noexcept(expression)还可以作为⼀个运算符去检测⼀个表达式是否会抛出异常，可能会则返回false，不会就返回true。

//double Divide(int a, int b) noexcept
//{
//	// 当b == 0时抛出异常
//	if (b == 0)
//	{
//		throw "Division by zero condition!";
//	}
//	return (double)a / (double)b;
//}
//
//int main()
//{
//	try
//	{
//		int len, time;
//		cin >> len >> time;
//		cout << Divide(len, time) << endl;
//	}
//	catch (const char* errmsg)
//	{
//		cout << errmsg << endl;
//	}
//	catch (...)
//	{
//		cout << "Unkown Exception" << endl;
//	}
//
//	int i = 0;
//	cout << noexcept(Divide(1, 2)) << endl;//false  可能会抛异常
//	cout << noexcept(Divide(1, 0)) << endl;//false
//	cout << noexcept(++i) << endl;//true 不会抛异常
//	return 0;
//}


//下⾯程序中我们可以看到，new了以后，我们也delete了，但是因为抛异常导致后⾯的delete没有得到
//执⾏，所以就内存泄漏了，所以我们需要new以后捕获异常，捕获到异常后delete内存，再把异常抛
//出，但是因为new本⾝也可能抛异常，连续的两个new和下⾯的Divide都可能会抛异常，让我们处理起
//来很⿇烦。智能指针放到这样的场景⾥⾯就让问题简单多了。
//double Divide(int a, int b)
//{
//	// 当b == 0时抛出异常
//	if (b == 0)
//	{
//		throw "Divide by zero condition!";
//	}
//	else
//	{
//		return (double)a / (double)b;
//	}
//}
//
//void Func()
//{
//	// 这⾥可以看到如果发⽣除0错误抛出异常，另外下⾯的array和array2没有得到释放。
//	// 所以这⾥捕获异常后并不处理异常，异常还是交给外⾯处理，这⾥捕获了再重新抛出去。
//	// 但是如果array2new的时候抛异常呢，就还需要套⼀层捕获释放逻辑，这⾥更好解决⽅案
//	// 是智能指针，否则代码太戳了
//	int* array1 = new int[10];
//	int* array2 = new int[10]; // 抛异常呢
//	try
//	{
//		int len, time;
//		cin >> len >> time;
//		cout << Divide(len, time) << endl;
//	}
//	catch (...)
//	{
//		cout << "delete []" << array1 << endl;
//		cout << "delete []" << array2 << endl;
//
//		delete[] array1;
//		delete[] array2;
//		throw; // 异常重新抛出，捕获到什么抛出什么
//	}
//	// ...
//	cout << "delete []" << array1 << endl;
//	delete[] array1;
//	cout << "delete []" << array2 << endl;
//	delete[] array2;
//}
//
//int main()
//{
//	try
//	{
//		Func();
//	}
//	catch (const char* errmsg)
//	{
//		cout << errmsg << endl;
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "未知异常" << endl;
//	}
//	return 0;
//}

//template <class T>
//class SmartPtr
//{
//public:
//	SmartPtr(T* ptr)
//		:_ptr(ptr)
//	{
//
//	}
//
//	~SmartPtr()
//	{
//		delete[] _ptr;
//	}
//
//	T& operator*()
//	{
//		return *_ptr;
//	}
//
//	T* operator->()
//	{
//		return _ptr;
//	}
//
//	T& operator[](size_t i)
//	{
//		return _ptr[i];
//	}
//
//private:
//	T* _ptr
//};
//
//double Divide(int a, int b)
//{
//	// 当b == 0时抛出异常
//	if (b == 0)
//	{
//		throw "Divide by zero condition!";
//	}
//	else
//	{
//		return (double)a / (double)b;
//	}
//}
//void Func()
//{
//	// 这⾥使⽤RAII的智能指针类管理new出来的数组以后，程序简单多了
//	SmartPtr<int> sp1 = new int[10];
//	SmartPtr<int> sp2 = new int[10];
//	for (size_t i = 0; i < 10; i++)
//	{
//		sp1[i] = sp2[i] = i;
//	}
//	int len, time;
//	cin >> len >> time;
//	cout << Divide(len, time) << endl;
//}
//int main()
//{
//	try
//	{
//		Func();
//	}
//	catch (const char* errmsg)
//	{
//		cout << errmsg << endl;
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "未知异常" << endl;
//	}
//	return 0;
//}

struct Date
{
	int _year;
	int _month;
	int _day;
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
	~Date()
	{
		cout << "~Date()" << endl;
	}
};


//int main()
//{
//	/*• auto_ptr是C++98时设计出来的智能指针，他的特点是拷⻉时把被拷⻉对象的资源的管理权转移给
//		拷⻉对象，这是⼀个⾮常糟糕的设计，因为他会到被拷⻉对象悬空，访问报错的问题*/
//	auto_ptr<Date> ap1(new Date);
//	// 拷⻉时，管理权限转移，被拷⻉对象ap1悬空
//	auto_ptr<Date> ap2(ap1);
//
//	// 空指针访问，ap1对象已经悬空
//	//ap1->_year++;
//
//
//	/*unique_ptr是C++11设计出来的智能指针，他的名字翻译出来是唯⼀指针，他的特点的不⽀持拷
//		⻉，只⽀持移动。如果不需要拷⻉的场景就⾮常建议使⽤他。*/
//	unique_ptr<Date> up1(new Date);
//	// 不⽀持拷⻉
//	//unique_ptr<Date> up2(up1);
//
//	// ⽀持移动，但是移动后up1也悬空，所以使⽤移动要谨慎
//	unique_ptr<Date> up3(move(up1));
//
//
//	/*shared_ptr是C++11设计出来的智能指针，他的名字翻译出来是共享指针，他的特点是⽀持拷⻉，
//		也⽀持移动。如果需要拷⻉的场景就需要使⽤他了。底层是⽤引⽤计数的⽅式实现的。*/
//	shared_ptr<Date> sp1(new Date);
//	// ⽀持拷⻉
//	shared_ptr<Date> sp2(sp1);
//	shared_ptr<Date> sp3(sp2);
//	cout << sp1.use_count() << endl;//引用计数 3
//
//	sp1->_year++;
//	cout << sp1->_year << endl;
//	cout << sp2->_year << endl;
//	cout << sp3->_year << endl;
//
//	// ⽀持移动，但是移动后sp1也悬空，所以使⽤移动要谨慎
//	shared_ptr<Date> sp4(move(sp1));
//
//	return 0;
//}

template<class T>
void DeleteArrayFunc(T* ptr)
{
	delete[] ptr;
}

template<class T>
class DeleteArray
{
public:
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};

class Fclose
{
public:
	void operator()(FILE* ptr)
	{
		cout << "fclose:" << ptr << endl;
		fclose(ptr);
	}
};



//智能指针析构时默认是进⾏delete释放资源，这也就意味着如果不是new出来的资源，交给智能指
//针管理，析构时就会崩溃。智能指针⽀持在构造时给⼀个删除器，所谓删除器本质就是⼀个可调⽤
//对象，这个可调⽤对象中实现你想要的释放资源的⽅式，当构造智能指针时，给了定制的删除器，
//在智能指针析构时就会调⽤删除器去释放资源
//int main()
//{
//	// 这样实现程序会崩溃
//	// unique_ptr<Date> up1(new Date[10]);
//	// shared_ptr<Date> sp1(new Date[10]);
//	// 
//	// 解决⽅案1
//	// 因为new[]经常使⽤，所以unique_ptr和shared_ptr
//	// 实现了⼀个特化版本，这个特化版本析构时⽤的delete[]
//	unique_ptr<Date[]> up1(new Date[5]);
//	shared_ptr<Date[]> sp1(new Date[5]);
//
//
//
//	// 解决⽅案2
//	// 
//	// 仿函数对象做删除器
//	//unique_ptr<Date, DeleteArray<Date>> up2(new Date[5], DeleteArray<Date>());
//	// 
//	// unique_ptr和shared_ptr⽀持删除器的⽅式有所不同
//	// unique_ptr是在类模板参数⽀持的，shared_ptr是构造函数参数⽀持的
//	// 这⾥没有使⽤相同的⽅式还是挺坑的
//	// 使⽤仿函数unique_ptr可以不在构造函数传递，因为仿函数类型构造的对象直接就可以调⽤
//	// 但是下⾯的函数指针和lambda的类型不可以
//	unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);
//	shared_ptr<Date> sp2(new Date[5], DeleteArray<Date>());
//
//	// 函数指针做删除器
//	unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);
//	shared_ptr<Date> sp3(new Date[5], DeleteArrayFunc<Date>);
//
//	// lambda表达式做删除器
//	auto delArrOBJ = [](Date* ptr) {delete[] ptr; };
//	unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);
//	shared_ptr<Date> sp4(new Date[5], delArrOBJ);
//	shared_ptr<Date> sp4(new Date[5], [](Date* ptr) {delete[] ptr; });
//
//
//
//	// 实现其他资源管理的删除器
//	shared_ptr<FILE> sp5(fopen("Test.cpp", "r"), Fclose());
//	shared_ptr<FILE> sp6(fopen("Test.cpp", "r"), [](FILE* ptr) {
//		cout << "fclose:" << ptr << endl;
//		fclose(ptr);
//		});
//
//	return 0;
//}

//shared_ptr 除了⽀持⽤指向资源的指针构造，还⽀持 make_shared ⽤初始化资源对象的值
//直接构造。
//• shared_ptr 和 unique_ptr 都⽀持了operator bool的类型转换，如果智能指针对象是⼀个
//空对象没有管理资源，则返回false，否则返回true，意味着我们可以直接把智能指针对象给if判断
//是否为空。
//• shared_ptr 和 unique_ptr 都得构造函数都使⽤explicit 修饰，防⽌普通指针隐式类型转换
//成智能指针对象。
//int main()
//{
//	shared_ptr<Date> sp1(new Date(2024, 9, 11));
//	shared_ptr<Date> sp2 = make_shared<Date>(2024, 9, 11);
//	auto sp3 = make_shared<Date>(2024, 9, 11);
//	shared_ptr<Date> sp4;
//	// if (sp1.operator bool())
//	if (sp1)
//		cout << "sp1 is not nullptr" << endl;
//	if (!sp4)
//		cout << "sp1 is nullptr" << endl;
//
//	// 报错
//	shared_ptr<Date> sp5 = new Date(2024, 9, 11);
//	unique_ptr<Date> sp6 = new Date(2024, 9, 11);
//
//	return 0;
//}

namespace ljh
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{

		}

		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		auto_ptr& operator=(const auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				if (_ptr)
				{
					delete _ptr;
				}

				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;

			}
		}

		T& operator*()
		{
			return *_ptr;
		}
			
		T* operator->()
		{
			return _ptr;
		}
			
		T& operator[](size_t i)
		{
			return _ptr[i];
		}

	private:
		T* _ptr;
	};


	template<class T>
	class unique_ptr
	{
	public:

		explicit unique_ptr(T* ptr)
			:_ptr(ptr)
		{

		}

		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;

		unique_ptr(unique_ptr<T>&& up)
			:_ptr(up._ptr)
		{
			up._ptr = nullptr;
		}

		unique_ptr<T>& operator=(unique_ptr<T>&& up)
		{
			delete _ptr;
			_ptr = up._ptr;
			up._ptr = nullptr;

			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
			
		}

	private:
		T* _ptr;
	};

	template<class T>
	class shared_ptr
	{
	public:
		explicit shared_ptr(T* ptr=nullptr)
			:_ptr(ptr)
			,_pcount(new int(1))
		{

		}

		template<class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			,_del(del)
			,_pcount(new int(1))
		{

		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcount(sp._pcount)
			,_del(sp._del)
		{
			(*_pcount)++;
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				if (--(*_pcount) == 0)
				{
					_del(_ptr);
					delete _pcount;
					_ptr = nullptr;
					_pcount = nullptr;
				}
				_ptr = sp._ptr;
				_pcount = sp._pcount;
				++(*_pcount);
				_del = sp._del;

			}

			return *this;
		}

		~shared_ptr()
		{
			if (--(*_pcount) == 0)
			{
				_del(_ptr);
				delete _pcount;
				_ptr =nullptr;
				_pcount = nullptr;
			}
		}

		T* get()const
		{
			return _ptr;
		}

		int use_count()const
		{
			return *_pcount;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

	private:
		T* _ptr;
		int* _pcount;
		function<void(T*)> _del= [](T* ptr) {delete ptr; };
	};


	// 需要注意的是我们这⾥实现的shared_ptr和weak_ptr都是以最简洁的⽅式实现的，
		// 只能满⾜基本的功能，这⾥的weak_ptr lock等功能是⽆法实现的，想要实现就要
		// 把shared_ptr和weak_ptr⼀起改了，把引⽤计数拿出来放到⼀个单独类型，shared_ptr
		// 和weak_ptr都要存储指向这个类的对象才能实现
	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
		{
		}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{
		}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}
		
	private:
		T * _ptr = nullptr;
	};
}

//int main()
//{
//	ljh::auto_ptr<Date> ap1(new Date);
//	// 拷⻉时，管理权限转移，被拷⻉对象ap1悬空
//	ljh::auto_ptr<Date> ap2(ap1);
//	 //空指针访问，ap1对象已经悬空
//	//ap1->_year++;
//	ljh::unique_ptr<Date> up1(new Date);
//	// 不⽀持拷⻉
//	//unique_ptr<Date> up2(up1);
//	// ⽀持移动，但是移动后up1也悬空，所以使⽤移动要谨慎
//	ljh::unique_ptr<Date> up3(move(up1));
//
//	ljh::shared_ptr<Date> sp1(new Date);
//	// ⽀持拷⻉
//	ljh::shared_ptr<Date> sp2(sp1);
//	ljh::shared_ptr<Date> sp3(sp2);
//	cout << sp1.use_count() << endl;
//	sp1->_year++;
//	cout << sp1->_year << endl;
//	cout << sp2->_year << endl;
//	cout << sp3->_year << endl;
//	return 0;
//}

//shared_ptr⼤多数情况下管理资源⾮常合适，⽀持RAII，也⽀持拷⻉。但是在循环引⽤的场景下会
//导致资源没得到释放内存泄漏，所以我们要认识循环引⽤的场景和资源没释放的原因，并且学会使
//⽤weak_ptr解决这种问题。
//• 如下图所述场景，n1和n2析构后，管理两个节点的引⽤计数减到1
//1. 右边的节点什么时候释放呢，左边节点中的_next管着呢，_next析构后，右边的节点就释放了。
//2. _next什么时候析构呢，_next是左边节点的的成员，左边节点释放，_next就析构了。
//3. 左边节点什么时候释放呢，左边节点由右边节点中的_prev管着呢，_prev析构后，左边的节点就释
//放了。
//4. _prev什么时候析构呢，_prev是右边节点的成员，右边节点释放，_prev就析构了。
//• ⾄此逻辑上成功形成回旋镖似的循环引⽤，谁都不会释放就形成了循环引⽤，导致内存泄漏
//• 把ListNode结构体中的_next和_prev改成weak_ptr，weak_ptr绑定到shared_ptr时不会增加它的
//引⽤计数，_next和_prev不参与资源释放管理逻辑，就成功打破了循环引⽤，解决了这⾥的问题

//struct ListNode
//{
//	int _data;
//	/*std::shared_ptr<ListNode> _next;
//	std::shared_ptr<ListNode> _prev;*/
//	// 这⾥改成weak_ptr，当n1->_next = n2;绑定shared_ptr时
//	// 不增加n2的引⽤计数，不参与资源释放的管理，就不会形成循环引⽤了
//	std::weak_ptr<ListNode> _next;
//	std::weak_ptr<ListNode> _prev;
//	~ListNode()
//	{
//		cout << "~ListNode()" << endl;
//	}
//};
//int main()
//{
//	// 循环引⽤ -- 内存泄露
//	std::shared_ptr<ListNode> n1(new ListNode);
//	std::shared_ptr<ListNode> n2(new ListNode);
//	cout << n1.use_count() << endl;
//	cout << n2.use_count() << endl;
//	n1->_next = n2;
//	n2->_prev = n1;
//	cout << n1.use_count() << endl;
//	cout << n2.use_count() << endl;
//	// weak_ptr不⽀持管理资源，不⽀持RAII
//	// weak_ptr是专⻔绑定shared_ptr，不增加他的引⽤计数，作为⼀些场景的辅助管理
//	//std::weak_ptr<ListNode> wp(new ListNode);
//	return 0;
//}


//weak_ptr不⽀持RAII，也不⽀持访问资源，所以我们看⽂档发现weak_ptr构造时不⽀持绑定到资
//源，只⽀持绑定到shared_ptr，绑定到shared_ptr时，不增加shared_ptr的引⽤计数，那么就可以
//解决上述的循环引⽤问题。
//• weak_ptr也没有重载operator* 和operator->等，因为他不参与资源管理，那么如果他绑定的
//shared_ptr已经释放了资源，那么他去访问资源就是很危险的。weak_ptr⽀持expired检查指向的
//资源是否过期，use_count也可获取shared_ptr的引⽤计数，weak_ptr想访问资源时，可以调⽤
//lock返回⼀个管理资源的shared_ptr，如果资源已经被释放，返回的shared_ptr是⼀个空对象，如
//果资源没有释放，则通过返回的shared_ptr访问资源是安全的。

int main()
{
	std::shared_ptr<string> sp1(new string("111111"));
	std::shared_ptr<string> sp2(sp1);
	std::weak_ptr<string> wp = sp1;
	cout << wp.expired() << endl;//0
	cout << wp.use_count() << endl;//2

	// sp1和sp2都指向了其他资源，则weak_ptr就过期了
	sp1 = make_shared<string>("222222");
	cout << wp.expired() << endl;//0
	cout << wp.use_count() << endl;//1

	sp2 = make_shared<string>("333333");
	cout << wp.expired() << endl;//1 -说明资源过期了
	cout << wp.use_count() << endl;//0 -说明没有指针指向111111了

	wp = sp1;
	//std::shared_ptr<string> sp3 = wp.lock();
	auto sp3 = wp.lock();//返回sp1，即222222，让sp3也指向他
	cout << wp.expired() << endl;//0
	cout << wp.use_count() << endl;//2
	*sp3 += "###";//222222###

	cout << *sp1 << endl;
	return 0;
}